package com.echo.code.thread.aqs;

import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * ReentrantReadWriteLockExam Created by baiyap on 2018/10/10.
 */
public class ReentrantReadWriteLockExam {

  public static Random rand = new Random(System.currentTimeMillis());

  public static void main(String[] args) {
    ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
    StringBuilder blackboard = new StringBuilder(100);

    ExecutorService service = Executors.newCachedThreadPool();
    service.execute(new Teacher(lock, "Chinese Teacher", blackboard));
    service.execute(new Teacher(lock, "English Teacher", blackboard));
    service.execute(new Student(lock, "Wang", blackboard));
    service.execute(new Student(lock, "Li", blackboard));
    service.execute(new Student(lock, "Liu", blackboard));
    service.shutdown();
  }

  private static class Teacher extends Thread {

    private ReentrantReadWriteLock lock;
    private StringBuilder blackboard;
    private AtomicInteger id = new AtomicInteger(0);

    private Teacher(ReentrantReadWriteLock lock, String name, StringBuilder blackboard) {
      super(name);
      this.lock = lock;
      this.blackboard = blackboard;
    }

    @Override
    public void run() {
      for (int i = 0; i < 1000; i++) {
        try {
          lock.writeLock().lock();
          System.out.println(getCurrentThreadName() + " get write lock");
          blackboard.delete(0, 99)
              .append(getCurrentThreadName() + " has written on the blackboard, number = " + id.getAndIncrement());
          System.out.println(getCurrentThreadName() + " write content:" + blackboard);
          TimeUnit.MILLISECONDS.sleep(rand.nextInt(500));
        } catch (InterruptedException e) {
          e.printStackTrace();
        } finally {
          lock.writeLock().unlock();
          System.out.println(getCurrentThreadName() + " release write lock");
        }
      }
    }

    private String getCurrentThreadName() {
      return Thread.currentThread().getName();
    }
  }

  private static class Student extends Thread {

    private ReentrantReadWriteLock lock;
    private StringBuilder blackboard;

    private Student(ReentrantReadWriteLock lock, String name, StringBuilder blackboard) {
      super(name);
      this.lock = lock;
      this.blackboard = blackboard;
    }

    @Override
    public void run() {
      for (int i = 0; i < 1000; i++) {
        try {
          lock.readLock().lock();
          System.out.println(getCurrentThreadName() + " get read lock");
          System.out.println(getCurrentThreadName() + " read the blackboard:" + blackboard);
          TimeUnit.MILLISECONDS.sleep(rand.nextInt(500));
        } catch (InterruptedException e) {
          e.printStackTrace();
        } finally {
          lock.readLock().unlock();
          System.out.println(getCurrentThreadName() + " release the read lock");
        }
      }
    }

    private String getCurrentThreadName() {
      return Thread.currentThread().getName();
    }
  }
}
